package jvn.client;

import java.rmi.RemoteException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import jvn.client.interfaces.JvnRemoteServerI;
import jvn.client.interfaces.StateI;
import jvn.coord.interfaces.JvnRemoteCoordI;

import org.apache.log4j.Logger;

public enum State implements StateI {

	/**
	 * No local lock
	 */
	NL {
		@Override
		public State lockRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.setObject(jo.getJvnRemoteCoord().jvnLockRead(jo.getJoi(), jo.getJvnRemoteServer()));
			jo.getReadLock().lock();
			return RLT;
		}

		@Override
		public State lockWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.setObject(jo.getJvnRemoteCoord().jvnLockWrite(jo.getJoi(), jo.getJvnRemoteServer()));
			jo.getWriteLock().lock();
			return WLT;
		}

		@Override
		public State unlock(JvnObjectImpl jo) throws RemoteException, JvnException {
			logger.info("No lock is taken");
			return NL;
		}

		@Override
		public State invalidateRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			return NL;
		}

		@Override
		public State invalidateWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			return NL;
		}

		@Override
		public State invalidateWriterForReader(JvnObjectImpl jo) throws RemoteException, JvnException {
			return this;
		}

	},
	/**
	 * Read lock cached
	 */
	RLC {
		@Override
		public State lockRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getReadLock().lock();
			return RLT;
		}

		@Override
		public State lockWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getJvnRemoteCoord().jvnLockWrite(jo.getJoi(), jo.getJvnRemoteServer());
			jo.getWriteLock().lock();
			return WLT;
		}

		@Override
		public State unlock(JvnObjectImpl jo) throws RemoteException, JvnException {
			logger.info("No lock is taken");
			return RLC;
		}

		@Override
		public State invalidateRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			return NL;
		}

		@Override
		public State invalidateWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			return NL;
		}

		@Override
		public State invalidateWriterForReader(JvnObjectImpl jo) throws RemoteException, JvnException {

			return this;
		}
	},
	/**
	 * Write lock cached
	 */
	WLC {
		@Override
		public State lockRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getReadLock().lock();
			return RLT_WLC;
		}

		@Override
		public State lockWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getWriteLock().lock();
			return WLT;
		}

		@Override
		public State unlock(JvnObjectImpl jo) throws RemoteException, JvnException {
			logger.info("No lock is taken");
			return WLC;
		}

		@Override
		public State invalidateRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			return NL;
		}

		@Override
		public State invalidateWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			return NL;
		}

		@Override
		public State invalidateWriterForReader(JvnObjectImpl jo) throws RemoteException, JvnException {

			return RLC;
		}
	},
	/**
	 * Read lock taken
	 */
	RLT {
		@Override
		public State lockRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			logger.info("You have already the read lock !");
			return RLT;
		}

		@Override
		public State lockWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.setObject(jo.getJvnRemoteCoord().jvnLockWrite(jo.getJoi(), jo.getJvnRemoteServer()));
			jo.getWriteLock().lock();
			jo.getReadLock().unlock();
			return WLT;
		}

		@Override
		public State unlock(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getReadLock().unlock();

			return RLC;
		}

		@Override
		public State invalidateRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getReadLock().lock();
			jo.getReadLock().unlock();
			return NL;
		}

		@Override
		public State invalidateWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getReadLock().lock();
			jo.getReadLock().unlock();
			return NL;
		}

		@Override
		public State invalidateWriterForReader(JvnObjectImpl jo) throws RemoteException, JvnException {
			return this;

		}
	},
	/**
	 * Write lock taken
	 */
	WLT {
		@Override
		public State lockRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			logger.info("Le verrou en écriture est déja aquis, celui en lecture l'est donc aussi !");
			// FIXME : étendre les fonctionnalités du programme
			return WLT;
		}

		@Override
		public State lockWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			logger.info("You have already the write lock !");
			return WLT;
		}

		@Override
		public State unlock(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getWriteLock().unlock();
			return WLC;
		}

		@Override
		public State invalidateRead(JvnObjectImpl jo) throws RemoteException, JvnException {

			jo.getWriteLock().lock();
			jo.getWriteLock().unlock();
			return NL;
		}

		@Override
		public State invalidateWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getWriteLock().lock();
			jo.getWriteLock().unlock();
			return NL;
		}

		@Override
		public State invalidateWriterForReader(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getWriteLock().lock();
			jo.getWriteLock().unlock();
			return RLC;

		}
	},
	/**
	 * Read lock taken ; Write lock cached
	 */
	RLT_WLC {
		@Override
		public State lockRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			logger.info("You have already the read lock !");
			return RLT_WLC;
		}

		@Override
		public State lockWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getWriteLock().lock();
			jo.getReadLock().unlock();
			return WLT;
		}

		@Override
		public State unlock(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getReadLock().unlock();
			return WLC;
		}

		@Override
		public State invalidateRead(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getReadLock().lock();
			jo.getReadLock().unlock();
			return NL;
		}

		@Override
		public State invalidateWrite(JvnObjectImpl jo) throws RemoteException, JvnException {
			jo.getReadLock().lock();
			jo.getReadLock().unlock();
			return NL;
		}

		@Override
		public State invalidateWriterForReader(JvnObjectImpl jo) throws RemoteException, JvnException {
			return RLT;
		}
	};

	
	final static private Logger logger = Logger.getLogger(State.class);

	public static StateI initCreate(JvnObjectImpl jo) {
		jo.getWriteLock().lock();
		return WLT;
	}

	public static StateI initLookUp(JvnObjectImpl jo) {
		return NL;
	}
}
